package com.minigame.card.mahjong.util;

import com.minigame.card.enums.Direction;
import com.minigame.card.mahjong.domain.MahjongCardPlayer;
import com.minigame.card.mahjong.enums.MahjongCard;

import java.util.*;

import static com.minigame.card.mahjong.enums.MahjongCard.*;
import static com.minigame.card.util.CardUtil.check;
import static com.minigame.card.util.CardUtil.init;
import static java.util.Comparator.comparing;

public class MahjongUtil {

    public static Map<Direction, MahjongCardPlayer> mahjiongInitPlayers(Direction host, List<MahjongCard> messList) {
        Map<Direction, List<MahjongCard>> playersUpCards = init(messList, host, 13, d -> false, comparing(MahjongCard::getIndex));
        Map<Direction, MahjongCardPlayer> playerMap = new LinkedHashMap<>();
        playersUpCards.forEach((direction, cards) -> {
            MahjongCardPlayer player = new MahjongCardPlayer();
            player.setDirection(direction);
            player.setUp(cards);
            playerMap.put(direction, player);
        });
        return playerMap;
    }
    /**
     * 打那张牌对应胡哪些牌
     * @param counts 所有牌计数数组
     * @param downTeams 落地成形牌
     */
    public static Map<Integer, int[]> tingMap(int[] counts, int[][] downTeams) {
        Map<Integer, int[]> tingMap = new LinkedHashMap<>();
        for (int i = 0; i < counts.length; i++) {
            if (counts[i] == 0) {
                continue;
            }
            counts[i]--;
            int[] huIndexes = tryMahjongTing(counts, downTeams);
            if (mahjongTing(huIndexes)) {
                tingMap.put(i, huIndexes);
            }
            counts[i]++;
        }
        return tingMap;
    }

    /**
     * 尝试听牌
     *
     * @param counts 所有牌计数数组
     * @param downTeams 落地成形牌
     * @return 胡牌索引数组
     */
    public static int[] tryMahjongTing(int[] counts, int[][] downTeams) {
        int[] tings = new int[0];
        for (int i = 0; i < counts.length; i++) {
            counts[i]++;
            if (mahjongHu(Arrays.copyOf(counts, counts.length), downTeams)) {
                tings = Arrays.copyOf(tings, tings.length + 1);
                tings[tings.length - 1] = i;
            }
            counts[i]--;
        }
        return tings;
    }

    /**
     * 能否听牌
     *
     * @param indexes 胡牌索引数组
     * @return 能否听牌
     */
    public static boolean mahjongTing(int[] indexes) {
        return indexes != null && indexes.length > 0;
    }

    /**
     * 大胡不要将, 小胡要将
     *
     * @param upCounts 牌计数数组
     * @param downTeams 下牌数组
     * @return 是否胡牌
     */
    public static boolean mahjongHu(int[] upCounts, int[][] downTeams) {
        if (specialMahjongHu(Arrays.copyOf(upCounts, upCounts.length), downTeams)) {
            return true;
        }
        return mahjongCommonHu(upCounts, true);
    }

    /**
     * 特殊胡牌: 风一色, 将一色, 清一色, 碰碰胡, 七对
     *
     * @param upCounts 手牌计数数组
     * @param downTeams 下牌数组
     * @return 是否大胡
     */
    public static boolean specialMahjongHu(int[] upCounts, int[][] downTeams) {
        Set<Integer> indexes = new HashSet<>();
        for (int i = 0; i < upCounts.length; i++) {
            if (upCounts[i] != 0) {
                indexes.add(i);
            }
        }
        if (downTeams != null) {
            for (int[] team : downTeams) {
                if (team != null) {
                    for (int index : team) {
                        indexes.add(index);
                    }
                }
            }
        }
        if (FENG_INDEXES.containsAll(indexes) || JIANG_INDEXES.containsAll(indexes)) {
            return true;
        }
        if (WAN_INDEXES.containsAll(indexes) || TIAO_INDEXES.containsAll(indexes) || BING_INDEXES.containsAll(indexes)) {
            return mahjongCommonHu(upCounts, false);
        }
        boolean allDouble = true;
        if (downTeams != null && downTeams.length > 0) {
            allDouble = false;
        } else {
            for (int upCount : upCounts) {
                if (upCount != 0 && upCount != 2) {
                    allDouble = false;
                    break;
                }
            }
        }
        if (allDouble) {
            return true;
        }
        boolean pongPong = true;
        if (downTeams != null) {
            for (int[] team : downTeams) {
                if (team != null && team.length > 0) {
                    if (Arrays.stream(team).distinct().count() != 1) {
                        pongPong = false;
                        break;
                    }
                }
            }
        }
        if (pongPong) {
            int jiang = 0;
            for (int upCount : upCounts) {
                if (upCount == 2) {
                    jiang++;
                } else if (upCount != 0 && upCount < 3) {
                    pongPong = false;
                    break;
                }
            }
            if (jiang != 1) {
                pongPong = false;
            }
        }
        return pongPong;
    }

    /**
     * 赖子赖完后判断可否胡牌
     *
     * @param upCounts       记录每张牌数量(不含赖子)
     * @param downTeams       下牌数组
     * @param ghostCount 赖子数量
     */
    public static boolean mahjongHu(int[] upCounts, int[][] downTeams, int ghostCount) {
        if (ghostCount == 0) {
            return mahjongHu(upCounts, downTeams);
        }
        for (int i = 0; i < upCounts.length; i++) {
            upCounts[i]++;
            ghostCount--;
            if (mahjongHu(upCounts, downTeams, ghostCount)) {
                return true;
            }
            upCounts[i]--;
            ghostCount++;
        }
        return false;
    }

    /**
     * 判断麻将普通胡牌
     * 0-8: 1-9万
     * 9-17: 1-9条
     * 18-26: 1-9筒
     * 27-33: 东南西北中发白
     *
     * @param upCounts        手牌中每张牌个数
     * @param requiredJiang 是否需要2,5,8将
     */
    public static boolean mahjongCommonHu(int[] upCounts, boolean requiredJiang) {
        for (int i = 0; i < upCounts.length; i++) {
            MahjongCard card = ofIndex(i);
            boolean jiang = card != null && card.isJiang();
            if (requiredJiang && !jiang) {
                continue;
            }
            if (upCounts[i] >= 2) {
                upCounts[i] -= 2;
                if (check(upCounts, DF.getIndex(), (index) -> index % 9 <= 6)) {
                    return true;
                }
                upCounts[i] += 2;
            }
        }
        return false;
    }

    /**
     * 尝试获取吃牌选项
     *
     * @param upCounts     计数数组
     * @param typeCount  每一类型有多少, 如万筒条各有9个
     * @param fetchIndex 获取值
     */
    public static int[][] tryMahjongCh(int[] upCounts, int typeCount, int fetchIndex) {
        return tryMahjongCh(upCounts, typeCount, -1, fetchIndex);
    }

    /**
     * 尝试获取吃牌选项
     *
     * @param upCounts       计数数组
     * @param typeCount    每一类型有多少, 如万筒条各有9个
     * @param ghostIndex 赖子
     * @param fetchIndex   获取值
     */
    public static int[][] tryMahjongCh(int[] upCounts, int typeCount, int ghostIndex, int fetchIndex) {
        int[][] selections = new int[0][];
        //第一个风作为边界
        int bound = DF.getIndex();
        if (ghostIndex == fetchIndex || fetchIndex > bound) {
            return selections;
        }
        int indexOfTypeCount = fetchIndex % typeCount;
        boolean leftBound = fetchIndex - 2 >= 0 && fetchIndex - 1 < bound
                && ghostIndex != fetchIndex - 2 && ghostIndex != fetchIndex - 1
                && upCounts[fetchIndex - 2] > 0 && upCounts[fetchIndex - 1] > 0;
        //左边张, 如一万二万吃三万
        if (leftBound) {
            if (indexOfTypeCount > 1) {
                selections = Arrays.copyOf(selections, selections.length + 1);
                selections[selections.length - 1] = new int[]{fetchIndex - 2, fetchIndex - 1, fetchIndex};
            }
        }
        //嵌张, 如一万三万吃二万
        boolean middle = fetchIndex - 1 >= 0 && fetchIndex + 1 < bound
                && ghostIndex != fetchIndex - 1 && ghostIndex != fetchIndex + 1
                && upCounts[fetchIndex - 1] > 0 && upCounts[fetchIndex + 1] > 0;
        if (middle) {
            if (indexOfTypeCount != 0 && indexOfTypeCount != typeCount - 1) {
                selections = Arrays.copyOf(selections, selections.length + 1);
                selections[selections.length - 1] = new int[]{fetchIndex - 1, fetchIndex, fetchIndex + 1};
            }
        }
        //右边张, 如八万九万吃七万
        boolean rightBound = fetchIndex + 2 < bound
                && ghostIndex != fetchIndex + 1 && ghostIndex != fetchIndex + 2
                && upCounts[fetchIndex + 1] > 0 && upCounts[fetchIndex + 2] > 0;
        if (rightBound) {
            if (indexOfTypeCount < typeCount - 2) {
                selections = Arrays.copyOf(selections, selections.length + 1);
                selections[selections.length - 1] = new int[]{fetchIndex, fetchIndex + 1, fetchIndex + 2};
            }
        }
        return selections;
    }

    /**
     * 能否吃
     *
     * @param selections 吃牌选项
     */
    public static boolean canMahjongCh(int[][] selections) {
        return selections != null && selections.length >= 1;
    }

    /**
     * 简单的ai, 打单张, 没有就返回 -1
     * @param counts 计数数组
     * @return 弃牌索引, 没有合适的则返回 -1
     */
    public static int aiDiscard(int[] counts) {
        for (int i = counts.length - 1; i >= 0; i--) {
            if (counts[i] == 1 && i >= DF.getIndex()) {
                return i;
            }
            if (i % 9 >= 2 && counts[i] == 1 && counts[i - 1] == 0 && counts[i - 2] == 0) {
                return i;
            }
            if (i % 9 == 1 && counts[i - 1] == 0 && counts[i] == 1 && counts[i + 1] == 0) {
                return i;
            }
            if (i % 9 == 0 && counts[i] == 1 && counts[i + 1] == 0 && counts[i + 2] == 0) {
                return i;
            }
        }
        for (int i = counts.length - 1; i >= 0; i--) {
            if (i % 9 >= 2 && counts[i] == 1 && counts[i - 1] == 0 && counts[i - 2] == 1) {
                return i;
            }
            if (i % 9 == 0 && counts[i] == 1 && counts[i + 1] == 0 && counts[i + 2] == 1) {
                return i;
            }
        }
        return -1;
    }
}
